﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using Microsoft.VisualStudio.Utilities;
using System.IO;
using System.Xml.Serialization;
using System.Xml;

namespace VsSpeakerCompanion
{
    /// <summary>
    /// Manages the Speaker settings.
    /// </summary>
    public class Settings
    {
        private const string DefaultSettingsPath = @"Settings.xml";
        private string settingsPath;

        private Dictionary<string, object> values;
        
        /// <summary>
        /// Creates a new instance of <see cref="Settings"/>
        /// </summary>
        public Settings()
            : this(DefaultSettingsPath)
        { }

        /// <summary>
        /// Gets the path where the settings is being saved
        /// </summary>
        public string SettingsPath
        {
            get { return this.settingsPath; }
        }

        /// <summary>
        /// Creates a new instance of <see cref="Settings"/>
        /// </summary>
        /// <param name="settingsPath"></param>
        public Settings(string settingsPath)
        {
            this.values = new Dictionary<string, object>();
            this.settingsPath = settingsPath;

            if (!Path.IsPathRooted(this.settingsPath))
            {
                this.settingsPath = Path.Combine(Path.GetDirectoryName(this.GetType().Assembly.Location), this.settingsPath);
            }
        }

        /// <summary>
        /// Updates the setting value for the specified key
        /// </summary>
        /// <param name="key"></param>
        /// <param name="value"></param>
        public virtual void Update(string key, object value)
        {
            if (this.values.ContainsKey(key))
            {
                this.values.Remove(key);
            }

            this.values.Add(key, value);
        }

        /// <summary>
        /// Gets the property value for the specified key
        /// </summary>
        /// <typeparam name="T"></typeparam>
        /// <param name="key"></param>
        /// <returns></returns>
        public virtual T Get<T>(string key)
        {
            if (!string.IsNullOrEmpty(key))
            {
                object value = null;
                if (this.values.TryGetValue(key, out value))
                {
                    return (T)value;
                }
            }

            return default(T);
        }

        /// <summary>
        /// Loads the settings
        /// </summary>
        public void Load()
        {
            if (File.Exists(settingsPath))
            {
                XmlSerializer serializer = new XmlSerializer(typeof(List<SettingValue>));

                using (XmlReader reader = XmlReader.Create(settingsPath))
                {
                    List<SettingValue> entries = serializer.Deserialize(reader) as List<SettingValue>;
                    values.Clear();
                    // Convert the list of settings to the dictionary
                    entries.ForEach(entry => values.Add(entry.Key, entry.Value));
                }
            }
        }

        private void EnsureSettingsFile()
        {
            if (File.Exists(settingsPath))
            {
                if ((File.GetAttributes(settingsPath) & FileAttributes.ReadOnly) != 0)
                {
                    File.SetAttributes(settingsPath, FileAttributes.Normal);
                }
            }
            else
            {
                string path = Path.GetDirectoryName(settingsPath);

                if (!Directory.Exists(path))
                {
                    Directory.CreateDirectory(path);
                }
            }
        }

        /// <summary>
        /// Saves the settings
        /// </summary>
        internal void Save()
        {
            EnsureSettingsFile();

            XmlSerializer serializer = new XmlSerializer(typeof(List<SettingValue>));
            XmlWriterSettings settings = new XmlWriterSettings();
            settings.Indent = true;

            using (XmlWriter writer = XmlWriter.Create(settingsPath, settings))
            {
                // Convert the dictionary of settings to a List and use the serializer to save them
                List<SettingValue> entries = new List<SettingValue>();
                this.values.ToList().ForEach(value => entries.Add(new SettingValue { Key = value.Key, Value = value.Value }));

                serializer.Serialize(writer, entries);
            }
        }
    }
}